import { BackupManager, BackupStatus } from './../BackupManager';
import { GraphNodeStatus } from './../../constants/Enums';
import { treeForEach } from './../common';
import { formatTime, getUid, storage, uint8ArrayToString, VueMap } from 'src/util/common';
import { storeKey, VERSION } from 'src/constants/constants';
import { createError, errorCode } from 'src/constants/ErrorConfig';
import { graphHelper } from 'src/api/GraphHelper';
import { ConfigManager } from '@/api/config/ConfigManager';
import { GraphContextHelper, MofModelRepositoryHelper, SysMLUtilHelper } from 'src/api/sysml-imports';
import { boxConfig, getFlatBoxConfig } from '@/api/config/BoxInfo';
import JSZip from 'jszip';
import graphNodeStatusFresher from 'src/components/cooperation/GraphNodeStatusFresher';

export interface TableData {
  columnWidths: Array<any>;
  visibleColumns: Array<any>;
  visibleRows: Array<UmlShape>;
}

export interface LocalFile {
  /**
   * 保存当前项目
   * @param project 当前项目
   */
  saveProject(project: Project): Promise<any>

  /**
   * 另存当前项目
   * @param project 当前项目
   */
  saveAsProject(project: Project, repalceCurrent:boolean): Promise<any>
  saveAsProjectXML(project: Project, repalceCurrent:boolean): Promise<any>

  /**
   * 打开项目
   * @param filePath 项目目标地址，如未提供，则打开文件选择器获取路径
   */
  openProject(filePath?: string): Promise<Project>

  importProject(filePath?: string): Promise<Project>

  importProjectXML(filePath?: string): Promise<Project>

  /**
   * 重载当前项目
   *
   */
  reloadProject(project:Project, authSave?:boolean): Promise<any>

  /**
   * 导入profile
   *
   * @param project 当前项目
   * @param filePaths 目标Profile路径数组
   */
  importProfile(project: Project, filePaths: string[]): Promise<any>

  /**
   * 获取项目初始化配置
   */
  getInitProject(): Project

  /**
   * 切换到对应的项目
   * @param project 目标项目
   */
  switchToProject(project: Project): any

  //// updateProject(project: Project): Promise<any>

  /**
   * 以默认配置存储项目
   * @param project 目标项目
   */
  saveProjectSilence(project: Project): Promise<any>

  exportProfile(graphNode: GraphNode): Promise<any>

  exportPackage(graphNode: GraphNode): Promise<any>

  /**
   * 打开Profile
   */
  openProfile(): Promise<any>

  /**
   * 导出需求表数据到Excel
   *
   * @param tableData 需求表数据
   * @param name 导出的Excel文件全限定名
   * @param format
   */
  exportRequirementTableExcel(tableData: TableData, name: string, format?: any): Promise<any>

  /**
   * 导出需求表到Reqif格式
   *
   * @param tableData 需求表数据
   * @param name 目标文件全限定名
   */
  exportRequirementTableReqif(tableData: TableData, name: string): Promise<any>
  openExcel(type: string): Promise<any>
  openReqif(type: string): Promise<any>
}

export class LocalFileService {

  private static readonly DEFAULT_PROFILE_FOLDER_PATH = '/statics';

  /**
   *
   * @param project
   */
  static parseProjectToBlob(project: Project): string {

    console.log('parseProjectToBlob -- start');
    try {
      const blob = SysMLUtilHelper.saveProject(project.config.id, SysMLUtilHelper.toArrayList(Array(project.metadata.sysml.model.model)));
      project.onlineConfig.nodeStatus = {};

      treeForEach([project.metadata.sysml.model], node => {
        //// 来自profile的元素和关系组节点不用记录node.status
        if (node.status !== GraphNodeStatus.FROM_PROFILE && node.getType() !== 'relationGroup') {
          //// console.log(node, 'ddddd');
          project.onlineConfig.nodeStatus[node.id] = node.status;
        }
      });
      project.config.dependencies = MofModelRepositoryHelper.getProjectDepencies(project.config.id, project.config.id);
      let projectData = {
        config: project.config,
        model: blob,
        onlineConfig: project.onlineConfig
      };
      return JSON.stringify(projectData);
    } catch (e) {
      throw createError(errorCode.SER_ERROR, e);
    } finally {
      console.log('parseProjectToBlob -- end');
    }

  }

  /**
   * 获取默认的依赖
   * @param selectModel
   */
  static getDefaultProfile(selectModel: any) {
    let dependencies = [];
    let uml = {
      dependencyId: 'UML Standard Profile',
      dependencyPath: '/statics/profile/UML Standard Profile.md3p'
    };
    let sysml = {
      dependencyId: 'SysML',
      dependencyPath: '/statics/profile/SysML.md3p'
    };
    let other = {
      dependencyId: 'M Design Profile',
      dependencyPath: '/statics/profile/M Design Profile.md3p'
    };
    dependencies.push(uml);
    dependencies.push(sysml);
    dependencies.push(other);

    if (selectModel != null) {
      let selectLocation = selectModel.getQualifiedName();
      switch (selectLocation) {
        case 'UML Standard Profile':
          dependencies = [];
          break;
        case 'SysML':
          dependencies = [uml];
          break;
        case 'M Design Profile':
          dependencies = [];
          dependencies.push(uml);
          dependencies.push(sysml);
      }
    }
    return dependencies;
  }

  static parseToProfileData(projectId: string, selectModel: any, path: string, fullPath: string, exportName: string) {
    let exportId = getUid();
    let dependencies = MofModelRepositoryHelper.getProjectDepencies(projectId, exportId);
    let selectLocation = selectModel.getQualifiedName();
    switch (selectLocation) {
      case 'UML Standard Profile':
        exportId = 'UML Standard Profile';
        break;
      case 'SysML':
        exportId = 'SysML';
        break;
      case 'M Design Profile':
        exportId = 'M Design Profile';
        break;
      default:
    }
    if (dependencies == null) {
      dependencies = LocalFileService.getDefaultProfile(selectModel);
    }
    const config = {
      rootIds: [selectModel.id],
      id: exportId,
      projectName: exportName,
      projectPath: path,
      projectFullPath: fullPath,
      projectDescription: '',
      createdDate: formatTime(Date.now()),
      updateDate: formatTime(Date.now()),
      version: VERSION,
      dependencies: dependencies
    };
    let selectModels = new Array(selectModel);
    console.log('导出的项目id:', projectId);
    let modelStr = SysMLUtilHelper.exportModule(projectId, SysMLUtilHelper.toArrayList(selectModels));
    let project = {
      config: config,
      model: modelStr
    };
    if (!modelStr) return;
    return JSON.stringify(project);
  }

  static parseProfileJsonString(fileData: ArrayBuffer): Promise<string> {
    return new Promise(rs => {
      const data = new Uint8Array(fileData);
      const str = uint8ArrayToString(data);
      return rs(str);
    });
  }

  /**
   * 读文件，基础方法
   *
   * @param path 文件路径
   */
  static readFile(path: string, option?:any): Promise<Buffer|string> {
    return new Promise((resolve, reject) => {
      window.$ele.fs.readFile(path, option, (e: Error, blob: Buffer) => {
        if (e) {
          reject(createError(errorCode.FILE_READ_ERROR));
        } else {
          resolve(blob);
        }
      });
    });
  }

  /**
   * 写文件，基础方法
   *
   * @param path  目标文件路径
   * @param blob  文件内容
   */
  static writeFile(path: string, blob: Blob | string | Uint8Array): Promise<boolean> {
    return new Promise((resolve, reject) => {
      window.$ele.fs.writeFile(path, blob, (e: Error) => {
        if (e) {
          //// console.error(e);
          reject(createError(errorCode.FILE_WRITE_ERROR, e ));
          return;
        }
        resolve(true);
      });
    });
  }

  static getInitProject() {
    const project: Project = {
      config: {
        id: '',
        rootIds: [],
        projectId: '',
        projectName: '',
        projectPath: '',
        projectFullPath: '',
        projectDescription: '',
        dependencies: [],
        createdDate: null,
        updateDate: null,
        version: VERSION,
        online: false,
        ignoreSyncReqIds: [],
        reqMap: {}
      },
      onlineConfig: {
        //// 服务端对应的版本id
        version: '',
        //// 已经检入的元素id
        //// packageStatus: new VueMap(), //// key=packageID , val=包状态
        privileges: {
          //// user privileges, the key is the user id
          //// 'abc': {
          ////   all: true,
          ////   read: [],
          ////   write: [],
          //// }
        },
        needSubmit: false, //// 是否需要提交更改
        nodeStatus: {} //// id:status //// 记录每个节点的状态，打开时还原，注意远程拉取下来的项目没有此属性
        //// newPackageIds: []

      },
      ui: {
        curEdgeType: '',
        selectedElement: undefined,
        recentDiagramIds: [],
        recentCreateDiagramTypes: [],
        breakpoints: new VueMap(), //// 断点列表
        graphTabs: [],
        activeTabId: '',
        currentTabId: '',
        modified: false,
        backupStatus: BackupStatus.NONE
      },
      metadata: {
        sysml: {
          di: {
            activityDiagram: '',
            diagrams: new VueMap()
          },
          model: undefined,
          pool: null
        }
      },
      configManager: undefined,
      backupManager: undefined
    };
    return project;
  }

  static async loadProjectData(projectBlob: ArrayBuffer | string): Promise<any> {
    console.log('loadProjectData - start');
    const oldActiveProjectId = app.activeProjectId;
    try {
      let projectJsonString;
      if (typeof projectBlob === 'string') {
        projectJsonString = projectBlob;
      } else {
        const data = new Uint8Array(projectBlob);
        projectJsonString = uint8ArrayToString(data);
      }
      const projectData = JSON.parse(projectJsonString);

      let project = this.getInitProject();
      project.config = projectData.config;
      //// 初始化当前项目的proxy信息
      app.proxy.projects.set(project.config.id, project);
      //// proxy.activeProjectId = project.config.id;
      //// 更新元素池
      MofModelRepositoryHelper.changePool(project.config.id);
      //// GraphContextHelper.initTypeKeys(ConfigManager.getBaseTypeNameConfig());
      //// GraphContextHelper.initBoxConfigs(getFlatBoxConfig(boxConfig));

      //// 加载依赖的Profile
      let idToJsonMap = new Map<string, string>();
      idToJsonMap.set(project.config.id, projectJsonString);
      project.metadata.sysml.model = await this.importProject(project, idToJsonMap);
      GraphContextHelper.setRootNode(project.config.id, project.metadata.sysml.model);

      console.log('顶层的graphNode:', project.metadata.sysml.model);
      //// TODO 针对在线项目，增添额外处理
      if (projectData.config.online === true) {
        if (projectData.onlineConfig.checkoutIds) project.onlineConfig.checkoutIds = projectData.onlineConfig.checkoutIds;
        if (projectData.onlineConfig.nodeStatus) project.onlineConfig.nodeStatus = projectData.onlineConfig.nodeStatus;
        if (projectData.onlineConfig.needSubmit) project.onlineConfig.needSubmit = projectData.onlineConfig.needSubmit;
        if (projectData.onlineConfig.version) project.onlineConfig.version = projectData.onlineConfig.version;
        //// if (projectData.onlineConfig.packageStatus)  project.onlineConfig.packageStatus = projectData.onlineConfig.packageStatus;
        if (projectData.onlineConfig.privileges) project.onlineConfig.privileges = projectData.onlineConfig.privileges;
      }

      project.configManager = new ConfigManager(project.config.id, project.metadata.sysml.model);
      project.backupManager = new BackupManager(project);

      GraphContextHelper.initBoxConfigs(project.configManager.boxConfig);
      GraphContextHelper.initTypeKeys(project.configManager.getTypeNameConfig());
      GraphContextHelper.initCompartmentConfig(project.configManager.compartmentConfig);
      GraphContextHelper.initDiagramInfo(project.configManager.diagramTypeConfig);
      project.configManager.initPropertyGroupConfig();

      SysMLUtilHelper.openProjectDiagram(project.config.id);

      //// if (app.activeProject) {
      ////   const checkoutIds = (app.activeProject.onlineConfig && app.activeProject.onlineConfig.checkoutIds) || [];
      ////   if (checkoutIds.length) {
      ////     project.onlineConfig.nodeStatus = {};
      ////     checkoutIds.forEach(item => {
      ////       project.onlineConfig.nodeStatus[item.packageId] = item.flag;
      ////     });
      ////   }
      //// }
      if (projectData.config.online === true) {
        treeForEach([project.metadata.sysml.model], node => {
          if (node.status !== GraphNodeStatus.FROM_PROFILE) {
            const pkgStatus = project.onlineConfig.nodeStatus && project.onlineConfig.nodeStatus[node.id];
            if (pkgStatus === undefined || pkgStatus === GraphNodeStatus.DEFAULT) {
              node.status = GraphNodeStatus.CHECKED_OUT;
            } else {
              node.status = pkgStatus;
            }
          }
        });
      }
      //// SysMLUtilHelper.initPool(model, project.config.id);
      //// console.log("初始化对象池");
      //// const rootNode = graphHelper.getRootNodeByProjectId(project.config.id)
      console.log('loadProjectData - end', project);

      if (project.backupManager.useBackup){
        project.backupManager.startAutoBackup();
      }

      return project;

    } catch (e) {
      console.error('新版本读取错误:', e);
      throw createError(errorCode.LOAD_PROJECT_ERROR, e);
      const project = this.getInitProject();
      await LocalFileService.loadProjectV1_1(project, projectBlob);      
      return project;
    } finally {
      MofModelRepositoryHelper.changePool(oldActiveProjectId);
    }

  }

  //// eslint-disable-next-line @typescript-eslint/camelcase
  static async loadProjectV1_1(project: Project, projectBlob: ArrayBuffer | string) {
    //// 加载系统自带Profile
    let defaultDependenciesPath = ['/statics/profile/UML Standard Profile.md3p', '/statics/profile/SysML.md3p', '/statics/profile/M Design Profile.md3p'];
    let map = new Map();
    for (let i = 0; i < defaultDependenciesPath.length; i++) {
      let filePath = defaultDependenciesPath[i];
      await LocalFileService.loadProfile(filePath, map);
    }
    let modelsArray = await JSZip.loadAsync(projectBlob, { base64: true }).then((zip) => {
      let modelsArray = [];
      const files = Object.values(zip.files);

      //// 获取旧版本的配置数据
      const configInfo = files.find(file => file.name.endsWith('.config')) as any;
      const configInfoStr = uint8ArrayToString(configInfo._data.compressedContent);
      console.log('旧版本配置数据:', configInfoStr);
      project.config = JSON.parse(configInfoStr);
      project.config.dependencies = LocalFileService.getDefaultProfile(null);

      //// 获取旧版本的模型数据
      const modeInfo = files.find(file => file.name.endsWith('.json')) as any;
      const modeInfoStr = uint8ArrayToString(modeInfo._data.compressedContent);
      console.log('旧版本模型数据:', modeInfoStr);
      modelsArray.push(modeInfoStr);

      const modeInfoJson = JSON.parse(modeInfoStr);
      project.config.rootIds = Array(modeInfoJson["id"]);
      project.config.version = VERSION;

      console.log('更改后的配置:', project.config);
      return modelsArray;
    });
    //// 获取新版本内置Profile
    map.forEach((value, key) => {
      if (value != null) {
        modelsArray.push(value);
      }
    });
    console.log('项目数据以及profile数据:', modelsArray);
    //// 初始化当前项目的proxy信息
    app.proxy.projects.set(project.config.id, project);
    //// 更新元素池
    MofModelRepositoryHelper.changePool(project.config.id);
    GraphContextHelper.initTypeKeys(ConfigManager.getBaseTypeNameConfig());
    //// 调用后台版本升级方法
    project.metadata.sysml.model = SysMLUtilHelper.versionUp(project.config.id, modelsArray);
    GraphContextHelper.setRootNode(project.config.id, project.metadata.sysml.model);
    console.log('配置信息:', project.config);
    project.configManager = new ConfigManager(project.config.id, project.metadata.sysml.model);
    GraphContextHelper.initBoxConfigs(project.configManager.boxConfig);
    GraphContextHelper.initTypeKeys(project.configManager.getTypeNameConfig());
    GraphContextHelper.initCompartmentConfig(project.configManager.compartmentConfig);
    GraphContextHelper.initDiagramInfo(project.configManager.diagramTypeConfig);
    project.configManager.initPropertyGroupConfig();

    SysMLUtilHelper.initLeafByOldDD(project.config.id);
  }

  //// static reloadProject(): Promise<any> {
  ////   return new Promise((res, rej) => {
  ////     const project = app.activeProject;
  ////     //// 初始化Diagram的Map
  ////     //// graphHelper.initDiagramJsMap(project.config.id);
  ////     //// 初始化当前项目的proxy信息
  ////     app.proxy.projects.set(project.config.id, project);
  ////     this.switchToProject(project);
  ////     res(true);
  ////   });
  //// }
  static removeProject(project:Project){
    const projectId = project.config.id;
    project.backupManager && project.backupManager.destroy();
    
    [...project.ui.graphTabs].forEach(tab => {
      if (tab.inModal){
        console.log('diagram-remove', { id: tab.id, projectId });
        app.$bus.emit('diagram-remove', { id: tab.id, projectId });
      }
    });

    //// diagram-remove
    project.ui.activeTabId = '';
    project.ui.currentTabId = '';
    
    app.proxy.projects.delete(projectId);
    //// SysMLUtilHelper.removePoolById(projectId);
    MofModelRepositoryHelper.removePool(projectId);
    project.metadata.sysml.model = null;
    project.metadata.sysml = null;
    project.ui = null;

    //// 清除watcher.value
    app.sysmlModel;
    app.activeGraphTab;
    app.configManager;
    app.di;

  }

  static async importProject(project: Project, idToJsonMap = new Map()): Promise<any> {
    const excludeDependencies = new Set<string>();
    const filePaths = [];
    if (project.config.dependencies !== undefined) {
      project.config.dependencies.forEach(it => {
        filePaths.push(it.dependencyPath);
      });
    }
    for (let i = 0; i < filePaths.length; i++) {
      let filePath = filePaths[i];
      console.log('依赖地址:', filePath);
      await this.loadProfile(filePath, idToJsonMap, excludeDependencies);
    }
    console.log('项目读取文件:', [...idToJsonMap.values()].filter(i => i !== true));
    return SysMLUtilHelper.openProject(project.config.id, [...idToJsonMap.values()].filter(i => i !== true));
  }

  static async importProjectWithoutInitPool(project: Project, idToJsonMap = new Map()): Promise<any> {
    const excludeDependencies = new Set<string>();
    const filePaths = [];
    if (project.config.dependencies !== undefined) {
      project.config.dependencies.forEach(it => {
        filePaths.push(it.dependencyPath);
      });
    }
    for (let i = 0; i < filePaths.length; i++) {
      let filePath = filePaths[i];
      console.log('依赖地址:', filePath);
      await this.loadProfile(filePath, idToJsonMap, excludeDependencies);
    }
    console.log('项目读取文件:', [...idToJsonMap.values()].filter(i => i !== true));
    return SysMLUtilHelper.importProject(project.config.id, [...idToJsonMap.values()].filter(i => i !== true));
  }

  static async loadProfile(filePath: string, map = new Map<string, string>(), excludeProfileIds: Set<string> = new Set<string>()) {
    //// TODO 修改inout的参数map
    let data;
    if (filePath.startsWith(this.DEFAULT_PROFILE_FOLDER_PATH)) {
      let dataObject = await app.$getLocal(filePath, {}, { responseType: 'arraybuffer' });
      data = dataObject.data;
    } else {
      //// TODO 讲对应的profile复制到默认路径下
      data = await LocalFileService.readFile((filePath));
    }
    let profileDataJsonString = await LocalFileService.parseProfileJsonString(data);
    const profileData = JSON.parse(profileDataJsonString);
    map.set(profileData.config.id, profileDataJsonString);
    if (profileData.config.dependencies) {
      for (let i = 0; i < profileData.config.dependencies.length; i++) {
        const dep = profileData.config.dependencies[i];
        if (map.get(dep.dependencyId)) return;
        await this.loadProfile(dep.dependencyPath, map, excludeProfileIds);
      }
    }
  }

  /**
   * 切换到新打开的项目,
   * @param {*} project
   */
  static async switchToProject(project: Project) {

    //// if (proxy.projects.get(project.config.id)) {
    ////   await app.$Confirm.popup({
    ////     content: '该项目已经被打开,将自动切换到该项目...',
    ////     cancelText: ''
    ////   }).catch((e:Error) => {
    ////     e && console.error(e);
    ////   }).finally(() => {
    ////     proxy.activeProjectId = project.config.id;

    ////   });
    //// } else {
    //// proxy.projects.set(project.config.id, project);
    console.log('Switch project', project);
    app.proxy.activeProjectId = project.config.id;

    //// }
    app.$bus.emit('project-switch', { id: project.config.id });
    graphNodeStatusFresher.startTimer();

    if (app.$route.path !== '/edit/default') {
      await app.$router.push('/edit/default');

    }

  }

  static async parseProjectData(projectBlob: ArrayBuffer | string): Promise<any> {
    console.log('parseProjectData - start');
    let projectJsonString;
    if (typeof projectBlob === 'string') {
      projectJsonString = projectBlob;
    } else {
      const data = new Uint8Array(projectBlob);
      projectJsonString = uint8ArrayToString(data);
    }
    const projectData = JSON.parse(projectJsonString);
    const project = this.getInitProject();
    project.config = projectData.config;
    
    //// 兼容旧项目
    project.config.ignoreSyncReqIds = project.config.ignoreSyncReqIds || [];
    project.config.reqMap = project.config.reqMap || {};

    //// 加载依赖的Profile
    let idToJsonMap = new Map<string, string>();
    idToJsonMap.set(project.config.id, projectJsonString);
    project.metadata.sysml.model = await this.importProjectWithoutInitPool(project, idToJsonMap);
    console.log('parseProjectData - end');
    return project;

  }

  /**
   * 合并两个项目
   *
   * @param extraProject
   * @param parentNode
   */
  static combineModel(extraProject: Project, parentNode: GraphNode) {
    //// TODO
  }

  /**
   * 更新最近打开记录
   * @param {Project} curProject 要更新的项目
   */
  static updateRecentProjects(curProject: Project): void {
    let config = curProject.config;
    config.updateDate = formatTime(Date.now());
    const project = {
      id: config.id,
      name: config.projectName,
      createdDate: config.createdDate,
      updateDate: config.updateDate,
      desc: config.projectDescription,
      path: config.projectPath,
      projectFullPath: config.projectFullPath
    };
    let recentProjects = storage.get(storeKey.recentProjects) || [];
    const index = recentProjects.findIndex(i => i.projectFullPath === config.projectFullPath);
    if (index === -1) {
      recentProjects.unshift(project);
    } else {
      recentProjects.splice(index, 1);
      recentProjects.unshift(project);

    }
    if (recentProjects.length > 8) {
      recentProjects = recentProjects.slice(0, 8);
    }
    storage.set(storeKey.recentProjects, recentProjects);
  }

  /**
   * 导入Profile
   *
   * @param project
   * @param filePaths
   */
  static async importProfile(project: Project, filePaths: string[]): Promise<any> {
    let projectDependencies = project.config.dependencies;
    const idToJsonStringMap = new Map<string, string>();
    const excludeProfileIds = new Set<string>();
    projectDependencies.forEach(dependency => {
      //// 过滤已经加载过的profile依赖
      excludeProfileIds.add(dependency.dependencyId);
    });
    for (let i = 0; i < filePaths.length; i++) {
      let filePath = filePaths[i];
      await LocalFileService.loadProfile(filePath, idToJsonStringMap, excludeProfileIds);
    }
    SysMLUtilHelper.importModule(project.config.id, [...idToJsonStringMap.values()]);
  }

}
